Options:
-h, --help Print this message
- --git Initialize a new git repository with a .gitignore
+ --no-git Don't initialize a new git repository
+ --git Initialize a new git repository, overriding a
+ global `git = false` configuration
--bin Use a binary instead of a library template
-v, --verbose Use verbose output
")
debug!("executing; cmd=cargo-new; args={}", os::args());
shell.set_verbose(options.flag_verbose);
- let Options { flag_git, flag_bin, arg_path, .. } = options;
+ let Options { flag_no_git, flag_bin, arg_path, flag_git, .. } = options;
let opts = ops::NewOptions {
+ no_git: flag_no_git,
git: flag_git,
path: arg_path.as_slice(),
bin: flag_bin,
use git2::{Repository, Config};
-use util::{CargoResult, human, ChainError};
+use util::{CargoResult, human, ChainError, config, internal};
use core::shell::MultiShell;
pub struct NewOptions<'a> {
+ pub no_git: bool,
pub git: bool,
pub bin: bool,
pub path: &'a str,
}
+struct CargoNewConfig {
+ name: Option<String>,
+ email: Option<String>,
+ git: Option<bool>,
+}
+
pub fn new(opts: NewOptions, _shell: &mut MultiShell) -> CargoResult<()> {
let path = os::getcwd().join(opts.path);
if path.exists() {
}
fn mk(path: &Path, name: &str, opts: &NewOptions) -> CargoResult<()> {
-
- if opts.git {
+ let cfg = try!(global_config());
+ if !opts.git && (opts.no_git || cfg.git == Some(false)) {
+ try!(fs::mkdir(path, io::UserRWX));
+ } else {
try!(Repository::init(path));
let mut gitignore = "/target\n".to_string();
if !opts.bin {
gitignore.push_str("/Cargo.lock\n");
}
try!(File::create(&path.join(".gitignore")).write(gitignore.as_bytes()));
- } else {
- try!(fs::mkdir(path, io::UserRWX));
}
- let author = try!(discover_author());
+ let (author_name, email) = try!(discover_author());
+ // Hoo boy, sure glad we've got exhaustivenes checking behind us.
+ let author = match (cfg.name, cfg.email, author_name, email) {
+ (Some(name), Some(email), _, _) |
+ (Some(name), None, _, Some(email)) |
+ (None, Some(email), name, _) |
+ (None, None, name, Some(email)) => format!("{} <{}>", name, email),
+ (Some(name), None, _, None) |
+ (None, None, name, None) => name,
+ };
+
try!(File::create(&path.join("Cargo.toml")).write_str(format!(
r#"[package]
Ok(())
}
-fn discover_author() -> CargoResult<String> {
+fn discover_author() -> CargoResult<(String, Option<String>)> {
let git_config = Config::open_default().ok();
let git_config = git_config.as_ref();
let name = git_config.and_then(|g| g.get_str("user.name").ok())
let name = name.as_slice().trim().to_string();
let email = email.map(|s| s.as_slice().trim().to_string());
- Ok(match (name, email) {
- (name, Some(email)) => format!("{} <{}>", name, email),
- (name, None) => name,
- })
+ Ok((name, email))
+}
+
+fn global_config() -> CargoResult<CargoNewConfig> {
+ let user_configs = try!(config::all_configs(os::getcwd()));
+ let mut cfg = CargoNewConfig {
+ name: None,
+ email: None,
+ git: None,
+ };
+ let cargo_new = match user_configs.find_equiv(&"cargo-new") {
+ None => return Ok(cfg),
+ Some(target) => try!(target.table().chain_error(|| {
+ internal("invalid configuration for the key `cargo-new`")
+ })),
+ };
+ cfg.name = match cargo_new.find_equiv(&"name") {
+ None => None,
+ Some(name) => {
+ Some(try!(name.string().chain_error(|| {
+ internal("invalid configuration for key `cargo-new.name`")
+ })).ref0().to_string())
+ }
+ };
+ cfg.email = match cargo_new.find_equiv(&"email") {
+ None => None,
+ Some(email) => {
+ Some(try!(email.string().chain_error(|| {
+ internal("invalid configuration for key `cargo-new.email`")
+ })).ref0().to_string())
+ }
+ };
+ cfg.git = match cargo_new.find_equiv(&"git") {
+ None => None,
+ Some(git) => {
+ Some(try!(git.boolean().chain_error(|| {
+ internal("invalid configuration for key `cargo-new.git`")
+ })).val0())
+ }
+ };
+
+ Ok(cfg)
}
String(String, Path),
List(Vec<(String, Path)>),
Table(HashMap<String, ConfigValue>),
+ Boolean(bool, Path),
}
impl fmt::Show for ConfigValue {
write!(f, "]")
}
Table(ref table) => write!(f, "{}", table),
+ Boolean(b, ref path) => write!(f, "{} (from {})", b, path.display()),
}
}
}
list.encode(s)
}
Table(ref table) => table.encode(s),
+ Boolean(b, _) => b.encode(s),
}
}
}
fn from_toml(path: &Path, toml: toml::Value) -> CargoResult<ConfigValue> {
match toml {
toml::String(val) => Ok(String(val, path.clone())),
+ toml::Boolean(b) => Ok(Boolean(b, path.clone())),
toml::Array(val) => {
Ok(List(try!(result::collect(val.move_iter().map(|toml| {
match toml {
fn merge(&mut self, from: ConfigValue) -> CargoResult<()> {
match (self, from) {
(me @ &String(..), from @ String(..)) => *me = from,
+ (me @ &Boolean(..), from @ Boolean(..)) => *me = from,
(&List(ref mut old), List(ref mut new)) => {
let new = mem::replace(new, Vec::new());
old.extend(new.move_iter());
pub fn string(&self) -> CargoResult<(&str, &Path)> {
match *self {
- Table(..) => Err(internal("expected a string, but found a table")),
- List(..) => Err(internal("expected a string, but found a list")),
String(ref s, ref p) => Ok((s.as_slice(), p)),
+ _ => Err(internal(format!("expected a string, but found a {}",
+ self.desc()))),
}
}
pub fn table(&self) -> CargoResult<&HashMap<String, ConfigValue>> {
match *self {
- String(..) => Err(internal("expected a table, but found a string")),
- List(..) => Err(internal("expected a table, but found a list")),
Table(ref table) => Ok(table),
+ _ => Err(internal(format!("expected a table, but found a {}",
+ self.desc()))),
}
}
pub fn list(&self) -> CargoResult<&[(String, Path)]> {
match *self {
- String(..) => Err(internal("expected a list, but found a string")),
- Table(..) => Err(internal("expected a list, but found a table")),
List(ref list) => Ok(list.as_slice()),
+ _ => Err(internal(format!("expected a list, but found a {}",
+ self.desc()))),
+ }
+ }
+
+ pub fn boolean(&self) -> CargoResult<(bool, &Path)> {
+ match *self {
+ Boolean(b, ref p) => Ok((b, p)),
+ _ => Err(internal(format!("expected a bool, but found a {}",
+ self.desc()))),
}
}
Table(..) => "table",
List(..) => "array",
String(..) => "string",
+ Boolean(..) => "boolean",
}
}
}
--- /dev/null
+---
+title: Configuration
+---
+
+This document will explain how cargo's configuration system works, as well as
+available keys or configuration. For configuration of a project through its
+manfest, see the [manifest format](manifest.html).
+
+# Hierarchical structure
+
+Cargo allows to have local configuration for a particular project or global
+configuration (like git). Cargo also extends this ability to a hirearchical
+strategy. If, for example, cargo were invoked in `/home/foo/bar/baz`, then the
+following configuration files would be probed for:
+
+* `/home/foo/bar/baz/.cargo/config`
+* `/home/foo/bar/.cargo/config`
+* `/home/foo/.cargo/config`
+* `/home/.cargo/config`
+* `/.cargo/config`
+
+With this structure you can specify local configuration per-project, and even
+possibly check it into version control. You can also specify personal default
+with a configuration file in your home directory.
+
+# Configuration Format
+
+All configuration is currently in the TOML format (like the manifest), with
+simple key-value pairs inside of sections (tables) which all get merged
+together.
+
+# Configuration keys
+
+All of the following keys are optional, and their defaults are listed as their
+value unless otherwise noted.
+
+```toml
+# An array of paths to local repositories which are to be used as overrides for
+# dependencies. For more information see the Cargo Guide.
+paths = [ "/path/to/override" ]
+
+[cargo-new]
+# This is your name/email to place in the `authors` section of a new Cargo.toml
+# that is generated. If not present, then `git` will be probed, and if that is
+# not present then `$USER` will be used (with no email).
+name = "..."
+email = "..."
+
+# By default `cargo new` will initialize a new git repository. This key can be
+# set to `false` to disable this behavior.
+git = true
+
+# For the following sections, $triple refers to any valid target triple, not the
+# literal string "$triple", and it will apply whenever that target triple is
+# being compiled to.
+[target]
+
+# For cargo builds which do not mention --target, these are the ar/linker which
+# are passed to rustc to use (via `-C ar=` and `-C linker=`). By default these
+# flags are not passed to the compiler.
+ar = ".."
+linker = ".."
+
+[target.$triple]
+# Similar to the above ar/linker configuration, but this only applies to when
+# the `$triple` is being compiled for.
+ar = ".."
+linker = ".."
+```
```
We're passing `--bin` because we're making a binary program: if we
-were making a library, we'd leave it off.
+were making a library, we'd leave it off. If you'd like to not initialize a new
+git repository as well (the default), you can also pass `--no-git`.
Let's check out what Cargo has generated for us:
this instance, we're just adding `conduit`, so it will be the only one that's
overridden.
+More information about local configuration can be found in the [configuration
+documentation](config.html).
+
# Tests
Cargo can run your tests with the `cargo test` command. Cargo runs tests in two
To start a new project with Cargo, use `cargo new`:
```shell
-$ cargo new hello_world --bin --git
+$ cargo new hello_world --bin
```
We're passing `--bin` because we're making a binary program: if we
-were making a library, we'd leave it off. We also pass `--git` to auto-generate
-a `.gitignore` and set up the git repository, but nothing will be committed.
+were making a library, we'd leave it off.
Let's check out what Cargo has generated for us:
<html>
<head>
<meta charset="utf-8">
-
+
<!-- Always force latest IE rendering engine or request Chrome Frame -->
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
-
+
<!-- Use title if it's in the page YAML frontmatter -->
<title><%= current_page.data.title || "The Middleman" %></title>
-
+
<%= stylesheet_link_tag "all" %>
<%= javascript_include_tag "all" %>
</head>
-
+
<body class="<%= page_classes %>">
<%= link_to image_tag("forkme.png", class: "fork-me"), "https://github.com/rust-lang/cargo" %>
<%= link_to image_tag("Cargo-Logo-Small.png", class: "logo"), "index.html" %>
<%= link_to "Guide", "guide.html" %> |
<%= link_to "Frequently Asked Questions", "faq.html" %> |
<%= link_to "Manifest Format", "manifest.html" %> |
- <%= link_to "Building Non-Rust Code", "native-build.html" %>
+ <%= link_to "Building Non-Rust Code", "native-build.html" %> |
+ <%= link_to "Configuration", "config.html" %>
</footer>
</main>
use std::os;
use support::{execs, paths, cargo_dir, ResultTest};
-use hamcrest::{assert_that, existing_file, existing_dir};
+use hamcrest::{assert_that, existing_file, existing_dir, is_not};
use cargo::util::{process, ProcessBuilder};
test!(simple_lib {
os::setenv("USER", "foo");
- assert_that(cargo_process("new").arg("foo"),
+ assert_that(cargo_process("new").arg("foo").arg("--no-git"),
execs().with_status(0));
assert_that(&paths::root().join("foo"), existing_dir());
assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
assert_that(&paths::root().join("foo/src/lib.rs"), existing_file());
+ assert_that(&paths::root().join("foo/.gitignore"), is_not(existing_file()));
assert_that(cargo_process("build").cwd(paths::root().join("foo")),
execs().with_status(0));
test!(simple_git {
os::setenv("USER", "foo");
- assert_that(cargo_process("new").arg("foo").arg("--git"),
+ assert_that(cargo_process("new").arg("foo"),
execs().with_status(0));
assert_that(&paths::root().join("foo"), existing_dir());
let toml = File::open(&toml).read_to_string().assert();
assert!(toml.as_slice().contains(r#"authors = ["bar <baz>"]"#));
})
+
+test!(author_prefers_cargo {
+ my_process("git").args(["config", "--global", "user.name", "bar"])
+ .exec().assert();
+ my_process("git").args(["config", "--global", "user.email", "baz"])
+ .exec().assert();
+ let root = paths::root();
+ fs::mkdir(&root.join(".cargo"), UserRWX).assert();
+ File::create(&root.join(".cargo/config")).write_str(r#"
+ [cargo-new]
+ name = "new-foo"
+ email = "new-bar"
+ git = false
+ "#).assert();
+
+ assert_that(cargo_process("new").arg("foo").env("USER", Some("foo")),
+ execs().with_status(0));
+
+ let toml = paths::root().join("foo/Cargo.toml");
+ let toml = File::open(&toml).read_to_string().assert();
+ assert!(toml.as_slice().contains(r#"authors = ["new-foo <new-bar>"]"#));
+ assert!(!root.join("foo/.gitignore").exists());
+})
+
+test!(git_prefers_command_line {
+ let root = paths::root();
+ fs::mkdir(&root.join(".cargo"), UserRWX).assert();
+ File::create(&root.join(".cargo/config")).write_str(r#"
+ [cargo-new]
+ git = false
+ name = "foo"
+ email = "bar"
+ "#).assert();
+
+ assert_that(cargo_process("new").arg("foo").arg("--git")
+ .env("USER", Some("foo")),
+ execs().with_status(0));
+ assert!(root.join("foo/.gitignore").exists());
+})